﻿using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static HardStyle.SimpleSplit.DataUnitConstant;
namespace HardStyle.SimpleSplit
{
    public class FileSplit
    {
        /// <summary>
        /// 分割文件按照数量分
        /// </summary>
        /// <param name="sourcePath">被分割文件路径</param>
        /// <param name="outDir">输出目录路径</param>
        /// <param name="fileCount">分成多少个文件(最少两个)</param>
        /// <param name="progress">进度报告cur/total/curProgress</param>
        /// <param name="suffix">自定义分割后文件的扩展</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static async Task SplitByQuantity(string sourcePath, string outDir, int fileCount, Action<int, int>? progress = null, string suffix = "SplitTemp", CancellationToken cancellationToken = default)
        {

            if (string.IsNullOrEmpty(sourcePath) || !File.Exists(sourcePath))
            {
                throw new Exception("source file not exists");
            }

            if (string.IsNullOrEmpty(outDir) || !Directory.Exists(outDir))
            {
                throw new Exception("outPath empty or directory not exists !");
            }

            if (fileCount < 2)
            {
                throw new Exception("fileCount min 2 !");
            }

            //文件基础名(不包括扩展名)
            string baseFileName = Path.GetFileNameWithoutExtension(sourcePath);
            //文件扩展名
            string extension = Path.GetExtension(sourcePath);
            //读入被分割的文件
            using var fs = new FileStream(sourcePath, FileMode.Open, FileAccess.Read);
            //计算每个文件分割的大小
            int sizeofEachFile = (int)Math.Ceiling((double)fs.Length / fileCount);
            //开始进行分割
            for (int i = 0; i < fileCount; i++)
            {
                cancellationToken.ThrowIfCancellationRequested();
                //输出文件全路径
                var fileName = $"{baseFileName}{extension}.{i.ToString().PadLeft(6, '0')}.{suffix}";
                //输出文件流
                using var outPutFile = new FileStream($@"{outDir}\{fileName}",
                    FileMode.OpenOrCreate, FileAccess.Write);

                //写入位置
                int bytesRead = 0;
                //缓存区
                byte[] buffer = new byte[sizeofEachFile];
                //写入分片文件
                if ((bytesRead = await fs.ReadAsync(buffer, 0, sizeofEachFile)) > 0)
                {
                    await outPutFile.WriteAsync(buffer, 0, bytesRead);
                }
                //进度报告
                progress?.Invoke(i + 1, fileCount);
            }
        }




        /// <summary>
        /// 分割文件按大小分割
        /// </summary>
        /// <param name="sourcePath">被分割文件路径</param>
        /// <param name="outDir">输出目录路径</param>
        /// <param name="size">文件碎片尺寸(单位字节)</param>
        /// <param name="progress">进度报告</param>
        /// <param name="suffix">自定义分割后文件后缀</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        public static async Task SplitBySize(string sourcePath, string outDir, long size, Action<int, int>? progress = null, string suffix = "SplitTemp", CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrEmpty(sourcePath) || !File.Exists(sourcePath))
            {
                throw new Exception("source file not exists");
            }

            if (string.IsNullOrEmpty(outDir) || !Directory.Exists(outDir))
            {
                throw new Exception("outPath empty or directory not exists !");
            }

            if (size < 0)
            {
                throw new Exception("Cannot be less than 0");
            }

            //被分割的文件流
            using var fs = new FileStream(sourcePath, FileMode.Open, FileAccess.Read);
            //分割文件的个数
            var fileCount = (int)Math.Ceiling((double)fs.Length / size);
            //文件基础名(不包括扩展名)
            string baseFileName = Path.GetFileNameWithoutExtension(sourcePath);
            //文件扩展名
            string extension = Path.GetExtension(sourcePath);

            //开始进行分割
            for (int i = 0; i < fileCount; i++)
            {
                cancellationToken.ThrowIfCancellationRequested();
                //输出文件全路径
                var fileName = $"{baseFileName}{extension}.{i.ToString().PadLeft(6, '0')}.{suffix}";
                //输出文件流
                using var outPutFile = new FileStream($@"{outDir}\{fileName}",
                    FileMode.OpenOrCreate, FileAccess.Write);

                //写入位置
                int bytesRead = 0;
                //缓存区
                byte[] buffer = new byte[size];
                //写入分片文件
                if ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await outPutFile.WriteAsync(buffer, 0, bytesRead);
                }
                //进度报告
                progress?.Invoke(i + 1, fileCount);
            }

        }




        /// <summary>
        /// 分割文件按大小分割
        /// </summary>
        /// <param name="sourcePath">被分割文件路径</param>
        /// <param name="outDir">输出目录路径</param>
        /// <param name="unit">分割单位</param>
        /// <param name="size">分割成多少个单位的大小</param>
        /// <param name="progress">进度报告</param>
        /// <param name="suffix">自定义分割后文件后缀</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static Task SplitBySize(string sourcePath, string outDir, DataUnit
            unit, long size, Action<int, int>? progress = null, string suffix = "SplitTemp", CancellationToken cancellationToken = default)
        {
            var s = 0L;
            //可能会出现算数溢出的情况
            try
            {
                checked
                {
                    switch (unit)
                    {
                        case DataUnit.KB:
                            s = KB * size;
                            break;
                        case DataUnit.MB:
                            s = MB * size;
                            break;
                        case DataUnit.GB:
                            s = GB * size;
                            break;
                        default:
                            throw new ArgumentNullException();
                    }
                }
                return SplitBySize(sourcePath, outDir, s, progress, suffix, cancellationToken);
            }
            catch (OverflowException)
            {
                throw;
            }
        }




        /// <summary>
        /// 合成文件
        /// </summary>
        /// <param name="sourceDir">资源目录</param>
        /// <param name="progress">进度报告</param>
        /// <param name="sourceSuffix">资源后缀名</param>
        /// <param name="isDeleteTemp">是否删除缓存文件</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns></returns>
        /// <exception cref="DirectoryNotFoundException">文件夹不存在</exception>
        /// <exception cref="Exception">该目录无可合成的文件</exception>
        public static async Task MergeFile(string sourceDir, Action<int, int, int>? progress = null, string sourceSuffix = "SplitTemp", bool isDeleteTemp = true, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(sourceDir) || !Directory.Exists(sourceDir))
            {
                throw new DirectoryNotFoundException("sourceDir empty or sourceDir not exists");
            }

            //选择可以合成的文件
            var tmpfiles = Directory.GetFiles(sourceDir, $"*.{sourceSuffix}");
            if (tmpfiles.Length == 0)
            {
                throw new Exception("There are no composable files!");
            }

            //输出文件流
            FileStream? outPutFile = null;
            //上一个文件,(可以满足多组分片文件合成的需求)
            string prevFileName = "";
            //用于计算进度
            var prePro = 0;

            //开始合成
            for (int i = 0; i < tmpfiles.Length; i++)
            {
                //发出取消信号进行取消操作
                cancellationToken.ThrowIfCancellationRequested();
                //当前文件全路径
                var tempFile = tmpfiles[i];
                //返回不具有扩展名的文件名
                var fileName = Path.GetFileNameWithoutExtension(tempFile);
                //基础文件名 0-.
                var curFileName = fileName.Substring(0, fileName.LastIndexOf('.'));

                //判断前一个文件名是否和当前相同,（可能会有多个文件相同）
                if (!prevFileName.Equals(curFileName))
                {
                    //当前文件和前一个文件不相同，并且前一个文件流未关闭就要先关闭之前的流
                    if (outPutFile != null)
                    {
                        await outPutFile.FlushAsync();
                        outPutFile.Close();
                    }
                    outPutFile = new FileStream(sourceDir + "\\" + curFileName, FileMode.OpenOrCreate, FileAccess.Write);
                }

                //tmp文件流
                using var inputTempFile = new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.Read);

                //读入字节个数
                var bytesRead = 0;
                //定义缓存区
                var buffer = new byte[MB];
                //不到达结尾则不为0
                while ((bytesRead = await inputTempFile.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await outPutFile!.WriteAsync(buffer, 0, bytesRead);
                    //计算进度，进度发生变化时才调用进度报告委托
                    var pro = (int)(inputTempFile.Position * 100.0 / inputTempFile.Length);
                    if (pro != prePro)
                    {
                        prePro = pro;
                        progress?.Invoke(i + 1, tmpfiles.Length, pro);
                    }
                }

                //关闭输出模板流
                inputTempFile.Close();
                //根据条件删除缓存文件
                if (isDeleteTemp is true)
                {
                    File.Delete(tempFile);
                }
                //更新上一个文件
                prevFileName = curFileName;
            }
            //使用完成关闭流
            outPutFile?.Close();
        }




























    }
}
